package me.weijing.common.http;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.channels.FileLock;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import me.weijing.common.util.FileUtil;
import me.weijing.common.util.JSONUtils;

import org.json.JSONObject;

import android.text.TextUtils;
import android.util.Log;

public class HttpRequestGet extends HttpRequest {
    private boolean bResource = false;
    private boolean bCallbackCalled = false;

    public HttpRequestGet(String origUrl, String url, HttpCallback callback, Map<String, String> extraHeader, RequestParams params) {
        super(origUrl, url, callback, extraHeader, params);
    }

    public HttpRequestGet(String origUrl, String url, Map<String, String> extraHeader) {
        super(origUrl, url, extraHeader);
    }

    public HttpRequestGet(String origUrl, String url, HttpProgressCallback callback, Map<String, String> extraHeader) {
        super(origUrl, url, callback, extraHeader);

    }

    public HttpRequestGet(String origUrl, String url, HttpProgressCallback callback, Map<String, String> extraHeader, boolean resource) {
        super(origUrl, url, callback, extraHeader);
        this.bResource = resource;
    }

    @Override
    public void execute() {
        byte[] data = request(bResource);
        if (callback != null && false == bCallbackCalled) {
            callback.interpret(data);
        }
    }

    public static final String kURLLostContinue = ".kURLLostContinue";
    public static final String kURLLostContinue_STARTPOS = ".kURLLostContinue_STARTPOS";
    public static final String kURLLostContinue_LOCALFILE = ".kURLLostContinue_LOCALFILE";
    public static final String kURLLostContinue_LOCALTMPFILE = ".kURLLostContinue_LOCALTMPFILE";
    public static final String kURLLostContinue_LOCALTMPMETAFILE = ".kURLLostContinue_LOCALTMPMETAFILE";
    public static final String kHTTPHeader_LASTMADIFY = ".kLASTMADIFY";
    public static final String kHTTPHeader_CONTENTLENGTH = ".kCONTENTLENGTH";
    public static final String kHTTPHeader_Expiration = ".kExpiration";
    public static final String kHTTPHeader_SupportRange = ".kSupportRange";

    public static boolean isUrlFileDownFinished(String url) {
        JSONObject metaObject = getUrlDownedFileInfo(url);
        if (null == metaObject)
            return false;
        String cacheFilePath = JSONUtils.getString(metaObject, kURLLostContinue_LOCALFILE);
        String cacheTmpFilePath = JSONUtils.getString(metaObject, kURLLostContinue_LOCALTMPFILE);
        String cacheFileMetaPath = JSONUtils.getString(metaObject, kURLLostContinue_LOCALTMPMETAFILE);
        if (TextUtils.isEmpty(cacheFilePath) || TextUtils.isEmpty(cacheTmpFilePath) || TextUtils.isEmpty(cacheFileMetaPath))
            return false;
        if (new File(cacheFilePath).exists())
            return true;
        int fileLen = JSONUtils.getInt(metaObject, kHTTPHeader_CONTENTLENGTH);
        if (metaObject.has(kHTTPHeader_CONTENTLENGTH) && fileLen == new File(cacheTmpFilePath).length())
            return true;
        return false;
    }

    public static JSONObject getUrlDownedFileInfo(String url) {
        String filePath = FileCacheStore.genCacheFilePath(url);
        String tempFilePath = FileCacheStore.getCacheFileTempPath(url);
        String tempFileMetaPath = FileCacheStore.getCacheFileMetaPath(url);
        if (null == filePath || null == tempFilePath || null == tempFileMetaPath)
            return null;
        File file = new File(filePath);
        File tempFile = new File(tempFilePath);
        File tempMetaFile = new File(tempFileMetaPath);
        if (file.exists()) {
            tempFile.delete();
            if (tempMetaFile.exists()) {
                String metaInfo = FileUtil.readFileContent(tempMetaFile);
                if (TextUtils.isEmpty(metaInfo) == false) {
                    try {
                        JSONObject result = new JSONObject(metaInfo);
                        long lastFileSize = JSONUtils.getLong(result, kHTTPHeader_CONTENTLENGTH);
                        if (lastFileSize == file.length()) {
                            result.put(kURLLostContinue, false);
                            result.put(kURLLostContinue_LOCALFILE, filePath);
                            result.put(kURLLostContinue_LOCALTMPFILE, tempFilePath);
                            result.put(kURLLostContinue_LOCALTMPMETAFILE, tempFileMetaPath);
                            return result;
                        }
                    } catch (Throwable t) {
                        t.printStackTrace();
                    }
                }
            }
        } else {
            if (tempFile.exists() && tempMetaFile.exists()) {
                String metaInfo = FileUtil.readFileContent(tempMetaFile);
                if (TextUtils.isEmpty(metaInfo) == false) {

                    try {
                        JSONObject result = new JSONObject(metaInfo);
                        int oldLen = result.getInt(kHTTPHeader_CONTENTLENGTH);
                        if (result.has(kHTTPHeader_SupportRange) && result.getBoolean(kHTTPHeader_SupportRange) == false)
                            oldLen = -1;
                        if (-1 != oldLen) {
                            result.put(kURLLostContinue, true);
                            result.put(kURLLostContinue_STARTPOS, (int) tempFile.length());
                            result.put(kURLLostContinue_LOCALFILE, filePath);
                            result.put(kURLLostContinue_LOCALTMPFILE, tempFilePath);
                            result.put(kURLLostContinue_LOCALTMPMETAFILE, tempFileMetaPath);
                            return result;
                        }
                    } catch (Throwable t) {
                        t.printStackTrace();
                    }
                }
            }
        }
        tempFile.delete();
        tempMetaFile.delete();
        try {
            JSONObject result = new JSONObject();
            result.put(kURLLostContinue, false);
            result.put(kURLLostContinue_LOCALFILE, filePath);
            result.put(kURLLostContinue_LOCALTMPFILE, tempFilePath);
            result.put(kURLLostContinue_LOCALTMPMETAFILE, tempFileMetaPath);
            return result;
        } catch (Throwable t) {
            t.printStackTrace();
        }
        return null;
    }

    /**
     * 通过Get方式获取资源
     * 
     * @return
     */
    public byte[] request(boolean isResource) {
        if (TextUtils.isEmpty(url)) {
            return null;
        }
        boolean bSupportRange = false;
        String cacheFilePath = "";
        String cacheTmpFilePath = "";
        String cacheFileMetaPath = "";
        String realUrl = url;
        HttpURLConnection conn = null;
        InputStream is = null;
        OutputStream bos = null;
        FileLock flock = null;
        FileOutputStream flockos = null;// 在多线程环境下使用FileLock实现对文件的锁功能,避免多个线程同时操作同一文件,
        String lockfpath = null;
        boolean isProgress = false;
        boolean bConnected = false;
        if (callback instanceof HttpProgressCallback) {
            isProgress = true;
        }
        try {
            if (params != null && !params.urlParams.isEmpty()) {
                if (realUrl.endsWith("?")) {
                    realUrl += (params.getParamString());
                } else {
                    realUrl += ('?' + params.getParamString());
                }
            }
            URL getUrl = new URL(realUrl);
            conn = (HttpURLConnection) getUrl.openConnection();
            conn.setRequestMethod("GET");
            conn.setAllowUserInteraction(false);
            conn.setConnectTimeout(TIMEOUT_CONNECT);
            conn.setReadTimeout(TIMEOUT_READ);
            conn.setRequestProperty("deviceId", deviceId);
            if (mExtraHeader != null) {
                Iterator<Map.Entry<String, String>> itr = mExtraHeader.entrySet().iterator();
                while (null != itr && itr.hasNext()) {
                    Map.Entry<String, String> entry = itr.next();
                    conn.setRequestProperty(entry.getKey(), entry.getValue());
                }
            }

            boolean bLostContinue = false;
            int startPos = 0;
            int endPos = -1;
            long expiration = 0;
            long lastModify = 0;
            long lastFileSize = 0;
            if (isResource) {
                JSONObject metaObject = getUrlDownedFileInfo(this.mOrigUrl);
                if (null == metaObject) {
                    bCallbackCalled = true;
                    if (null != callback)
                        callback.serverFail(-1);
                    return null;
                }
                cacheFilePath = metaObject.getString(kURLLostContinue_LOCALFILE);
                cacheTmpFilePath = metaObject.getString(kURLLostContinue_LOCALTMPFILE);
                cacheFileMetaPath = metaObject.getString(kURLLostContinue_LOCALTMPMETAFILE);

                lockfpath = cacheFileMetaPath + ".lock";
                flockos = FileUtil.openFile4Write(lockfpath);
                if (null != flockos) {
                    flock = flockos.getChannel().lock();
                }

                metaObject = getUrlDownedFileInfo(this.mOrigUrl);
                lastFileSize = JSONUtils.getLong(metaObject, kHTTPHeader_CONTENTLENGTH);
                bLostContinue = metaObject.getBoolean(kURLLostContinue);
                if (bLostContinue) {
                    startPos = JSONUtils.getInt(metaObject, kURLLostContinue_STARTPOS);
                    endPos = JSONUtils.getInt(metaObject, kHTTPHeader_CONTENTLENGTH);
                    if (0 == endPos)
                        endPos = -1;
                    conn.setRequestProperty("Range", "bytes=" + String.valueOf(startPos) + "-");
                } else {
                    FileUtil.deleteFile(cacheTmpFilePath);
                    expiration = JSONUtils.getLong(metaObject, kHTTPHeader_Expiration);
                    lastModify = JSONUtils.getLong(metaObject, kHTTPHeader_LASTMADIFY);
                    if (metaObject.has(kHTTPHeader_LASTMADIFY)) {
                        conn.setIfModifiedSince(lastModify);
                    }
                }
            }
            conn.connect();
            bConnected = true;
            int respCode = conn.getResponseCode();
            Log.i("TAG", "response Code is   " + conn.getResponseCode() + "");
            if (respCode == HttpURLConnection.HTTP_OK || (HttpURLConnection.HTTP_PARTIAL == respCode && bLostContinue)) {
                is = conn.getInputStream();
                int fileSize = conn.getContentLength();
                long newLastModify = conn.getLastModified();
                if (HttpURLConnection.HTTP_PARTIAL == respCode) {
                    fileSize = endPos;
                    if (newLastModify != lastModify) {
                        FileUtil.deleteFile(cacheFilePath);
                        FileUtil.deleteFile(cacheTmpFilePath);
                        FileUtil.deleteFile(cacheFileMetaPath);
                        bCallbackCalled = true;
                        if (null != callback)
                            callback.serverFail(-1);
                        return null;
                    }
                }
                if (isResource) {
                    byte[] data = null;

                    if (fileSize > HttpRequest.getNetLostContinueFileLimit()) {

                        JSONObject jsonResult = new JSONObject();

                        Map<String, List<String>> rawresponseHeaders = conn.getHeaderFields();

                        Map<String, List<String>> responseHeaders = new HashMap<String, List<String>>();
                        Iterator<String> keyItr = rawresponseHeaders.keySet().iterator();
                        while (keyItr.hasNext()) {
                            String key = keyItr.next();
                            if (null == key) {
                                continue;
                            }
                            List<String> valList = rawresponseHeaders.get(key);
                            responseHeaders.put(key.toLowerCase(), valList);
                        }
                        if (responseHeaders != null) {
                            // bSupportRange=responseHeaders.containsKey("Accept-Ranges");
                            if (responseHeaders.containsKey("accept-ranges")) {
                                bSupportRange = true;
                                List<String> rangeList = responseHeaders.get("accept-ranges");
                                for (String rangeRsp : rangeList) {
                                    if (rangeRsp.equals("none")) {
                                        bSupportRange = false;
                                        break;
                                    }
                                }
                            }
                            if (bLostContinue && false == responseHeaders.containsKey("content-range")) {
                                startPos = 0;
                                FileUtil.deleteFile(cacheTmpFilePath);
                                bSupportRange = false;
                            }

                        }
                        jsonResult.put(kHTTPHeader_LASTMADIFY, newLastModify);
                        jsonResult.put(kHTTPHeader_CONTENTLENGTH, fileSize);
                        jsonResult.put(kHTTPHeader_Expiration, conn.getExpiration());
                        jsonResult.put(kHTTPHeader_SupportRange, bSupportRange);
                        FileUtil.writeFile(cacheFileMetaPath, jsonResult.toString());
                        jsonResult = null;
                    } else {
                        cacheTmpFilePath = FileCacheStore.genTempCacheFile();
                    }

                    // String cacheFilePath=FileCacheStore.genNewCacheFile();
                    bos = FileUtil.openFile4Write(cacheTmpFilePath, true);//

                    if (null == bos) {
                        bCallbackCalled = true;
                        if (null != callback)
                            callback.serverFail(-1);
                        return null;
                    }
                    if (-1 == fileSize) {
                        fileSize = 1024 * 1024 * 30;
                    }

                    int readTotalLength = startPos;

                    if (data == null) {
                        data = new byte[4096];
                    }
                    int oldProgress = 0;
                    int icount = 0;
                    while (true) {

                        int readLength = is.read(data);
                        if (readLength == -1) {
                            if (isProgress && null != callback) {
                                ((HttpProgressCallback) callback).progressPublish(100);
                            }
                            break;
                        }

                        if (null != callback) {
                            if (callback.isCanceled()) {
                                bCallbackCalled = true;
                                // FileUtil.deleteFile(cacheTmpFilePath);
                                return null;
                            }
                        }

                        bos.write(data, 0, readLength);

                        icount++;
                        if (icount > UPLOAD_FILE_SIZE) {
                            bos.flush();
                            icount = 0;
                        }

                        if (isProgress) {
                            readTotalLength += readLength;
                            if (null != callback) {
                                int newProgress = readTotalLength * 100 / fileSize;
                                if (newProgress > 99)
                                    newProgress = 99;
                                if (oldProgress != newProgress) {
                                    ((HttpProgressCallback) callback).progressPublish(newProgress);
                                }
                                oldProgress = newProgress;
                            }
                        }
                    }
                    bos.flush();
                    // return cacheFilePath.getBytes();

                    if (null != callback) {
                        bCallbackCalled = true;
                        callback.interpret(cacheTmpFilePath.getBytes(), conn.getHeaderFields());
                        return null;
                    }
                } else {
                    bos = new ByteArrayOutputStream();
                    byte[] data = null;
                    if (data == null) {
                        data = new byte[1024];
                    }
                    int icount = 0;
                    while (true) {
                        int iret = is.read(data);
                        if (iret < 0)
                            break;
                        bos.write(data, 0, iret);
                        icount++;
                        if (icount > UPLOAD_FILE_SIZE) {
                            bos.flush();
                            icount = 0;
                        }

                    }
                    bos.flush();
                    return ((ByteArrayOutputStream) bos).toByteArray();
                }
            } else {
                if (conn.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED && isResource) {
                    if (lastFileSize != conn.getContentLength() || lastModify != conn.getLastModified()) {
                        FileUtil.deleteFile(cacheTmpFilePath);
                        FileUtil.deleteFile(cacheFileMetaPath);
                        FileUtil.deleteFile(cacheFilePath);
                        throw new Exception("");
                    }
                    if (null != callback) {
                        bCallbackCalled = true;
                        if (isProgress) {
                            ((HttpProgressCallback) callback).progressPublish(100);
                        }
                        FileUtil.renameFile(cacheFilePath, cacheTmpFilePath);
                        callback.interpret(cacheTmpFilePath.getBytes(), conn.getHeaderFields());
                        return null;
                    }
                }
                FileUtil.deleteFile(cacheTmpFilePath);
                FileUtil.deleteFile(cacheFileMetaPath);
                bCallbackCalled = true;
                if (null != callback)
                    callback.serverFail(conn.getResponseCode());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } catch (Throwable t) {
            t.printStackTrace();
        } finally {
            if (null != flock) {
                try {
                    flock.release();
                } catch (Throwable e) {
                    e.printStackTrace();
                }
                flock = null;
                if (null != flockos) {
                    try {
                        flockos.close();
                    } catch (Throwable t) {

                    }
                    flockos = null;
                }
                if (null != lockfpath) {
                    try {
                        FileUtil.deleteFile(lockfpath);
                    } catch (Throwable t) {

                    }
                    lockfpath = null;
                }
            }

            if (bos != null) {
                try {
                    bos.close();
                } catch (Throwable e) {

                    e.printStackTrace();
                }
            }
            if (is != null) {
                try {
                    is.close();
                } catch (Throwable e) {

                    e.printStackTrace();
                }
            }
            if (conn != null) {
                conn.disconnect();
            }
        }
        if (isResource) {
            if (bSupportRange == false && bConnected) {
                FileUtil.deleteFile(cacheTmpFilePath);
            }

            if (null != callback) {
                bCallbackCalled = true;
                FileUtil.renameFile(cacheFilePath, cacheTmpFilePath);
                if (isProgress) {
                    ((HttpProgressCallback) callback).progressPublish(100);
                }
                callback.interpret(cacheTmpFilePath.getBytes(), conn.getHeaderFields());
                return null;
            }
        }

        if (!bCallbackCalled) {
            if (null != callback)
                callback.serverFail(AsyncHttpRequestBase.FAILTYPE_SERVER_FAIL);
            bCallbackCalled = true;
        }
        return null;
    }
}
